This is an R Markdown
Notebook. Each section of the code is then explained.
First of all import the libraries needed
#install.packages(c("datavolley", "ovlytics"))
library(datavolley)
library(ggplot2)
library(dplyr)
library(ovlytics)
Import the file you are interested in considering more than one
match, you have to import all the folder
filename <- "C:/Users/mirko/Documents/GitHub/CuneoWebsite.io/Assets/Cuneo-Bergamo_cuneo.dvw"
#d <- dir("C:/Users/mirko/OneDrive - Politecnico di Milano/Altro/Volley/Conco2324/Parella Torino/Ritorno/", pattern = "dvw$", full.names = TRUE)
teamName = 'HONDA OLIVERO S.BERNARDO CUNEO'
x <- dv_read(filename)
Avvertimento: stri_enc_detect2 is deprecated and will be removed in a future release of 'stringi'.
serve_idx <- find_serves(plays(x))
table(plays(x)$team[serve_idx])
HONDA OLIVERO S.BERNARDO CUNEO VOLLEY BERGAMO 1991
98 93
Funzioni utili
## find rows where a single player is on court
player_on_court <- function(x, target_player_id, team = NULL) {
if (!is.null(team)) team <- match.arg(team, c("home", "visiting"))
## 'team' is optional here, if NULL then we look at both home and visiting teams
idx <- rep(FALSE, nrow(x))
if (is.null(team) || team == "home") {
idx <- idx | x$home_player_id1 == target_player_id | x$home_player_id2 == target_player_id | x$home_player_id3 == target_player_id |
x$home_player_id4 == target_player_id | x$home_player_id5 == target_player_id | x$home_player_id6 == target_player_id
}
if (is.null(team) || team == "visiting") {
idx <- idx | x$visiting_player_id1 == target_player_id | x$visiting_player_id2 == target_player_id | x$visiting_player_id3 == target_player_id |
x$visiting_player_id4 == target_player_id | x$visiting_player_id5 == target_player_id | x$visiting_player_id6 == target_player_id
}
idx[is.na(idx)] <- FALSE
idx
}
## find rows where any of our target players are on court
any_player_on_court <- function(x, target_player_ids, team = NULL) {
## for each target player, find rows where they are on court
out <- lapply(target_player_ids, function(pid) player_on_court(x, target_player_id = pid, team = team))
## and now find rows where ANY of those players were on court
apply(do.call(cbind, out), 1, any)
}
## find rows where all of our target players are on court
all_players_on_court <- function(x, target_player_ids, team = NULL) {
## for each target player, find rows where they are on court
out <- lapply(target_player_ids, function(pid) player_on_court(x, target_player_id = pid, team = team))
## and now find rows where ALL of those players were on court
apply(do.call(cbind, out), 1, all)
}
d <- dir("C:/Users/mirko/Documents/GitHub/CuneoWebsite.io/Assets/", pattern = "dvw$", full.names = TRUE)
lx <- list()
## read each file
for (fi in seq_along(d)) lx[[fi]] <- dv_read(d[fi], insert_technical_timeouts = FALSE)
Avvertimento: stri_enc_detect2 is deprecated and will be removed in a future release of 'stringi'.
## now extract the play-by-play component from each and bind them together
px <- list()
for (fi in seq_along(lx)) px[[fi]] <- plays(lx[[fi]])
px <- do.call(rbind, px)
Rendimento in Battuta
#, end_zone == 5
table_data <- px %>%
dplyr::filter(skill == "Serve", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_battute = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = (count_positive + count_perfette)/N_battute,
efficienza = (count_positive + count_perfette - count_errori)/N_battute,
)
table_data
library(kableExtra)
# Apply custom CSS styling to the entire table
# Reorder columns to make positività the second column
table_data <- table_data %>%
#select(-efficienza) %>%
select(1, 7, everything())
# Apply custom CSS styling to the entire table
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
column_spec(2, background = ifelse(table_data$efficienza >= 0.3, "lightgreen",ifelse(table_data$efficienza > 0.2 & table_data$efficienza < 0.3, "yellow", "lightcoral")))
#row_spec(which(table_data$efficienza > 0.2 & table_data$efficienza < 0.3), background = "yellow") %>%
#row_spec(which(table_data$efficienza <= 0.2), background = "lightcoral")
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Battuta_tab.html")
library(plotly)
Avvertimento: il pacchetto ‘plotly’ è stato creato con R versione 4.3.2Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Caricamento pacchetto: ‘plotly’
Il seguente oggetto è mascherato da ‘package:ggplot2’:
last_plot
Il seguente oggetto è mascherato da ‘package:stats’:
filter
Il seguente oggetto è mascherato da ‘package:graphics’:
layout
fig <- plot_ly(table_data,
x = ~positività,
y = ~efficienza,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività</b>: %{x}',
'<br><b>Efficienza</b>: %{y}',
'<br><b>Battuta</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_battute, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Battuta',
xaxis = list(title = 'Positività', showgrid = TRUE),
yaxis = list(title = 'Efficienza', showgrid = TRUE)
)
fig
library(htmlwidgets)
Avvertimento: il pacchetto ‘htmlwidgets’ è stato creato con R versione 4.3.2
# Assuming 'fig' is your Plotly figure
saveWidget(fig, "Battuta.html")
Rendimento in Ricezione
Ora analizziamo la ricezione:
#, end_zone == 5
table_data <- px %>%
dplyr::filter(skill == "Reception", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_receptions = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
#count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = (count_positive + count_perfette)/N_receptions,
efficienza = (count_positive + count_perfette - count_errori)/N_receptions,
)
Error in `dplyr::summarize()`:
ℹ In argument: `efficienza = (count_positive + count_perfette - count_errori)/N_receptions`.
ℹ In group 1: `player_name = "Alice Tanase"`.
Caused by error:
! object 'count_errori' not found
Backtrace:
1. ... %>% ...
3. dplyr:::summarise.grouped_df(...)
4. dplyr:::summarise_cols(.data, dplyr_quosures(...), by, "summarise")
6. dplyr:::map(quosures, summarise_eval_one, mask = mask)
7. base::lapply(.x, .f, ...)
8. dplyr (local) FUN(X[[i]], ...)
9. mask$eval_all_summarise(quo)
10. dplyr (local) eval()
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
row_spec(which(table_data$efficienza >= 0.3), background = "lightgreen") %>%
row_spec(which(table_data$efficienza > 0.2 & table_data$efficienza < 0.3), background = "yellow") %>%
row_spec(which(table_data$efficienza <= 0.2), background = "lightcoral")
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Ricezione_tab.html")
fig <- plot_ly(table_data,
x = ~positività,
y = ~efficienza,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività</b>: %{x}',
'<br><b>Efficienza</b>: %{y}',
'<br><b>Ricezione</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_receptions, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Ricezione',
xaxis = list(title = 'Positività', showgrid = TRUE),
yaxis = list(title = 'Efficienza', showgrid = TRUE)
)
fig
saveWidget(fig, "Ricezione.html")
Rendimento in Attacco
# end_zone == 5
table_data <- px %>%
dplyr::filter(skill == "Attack", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_attacks = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = (count_positive + count_perfette)/N_attacks,
efficienza = (count_positive + count_perfette - count_errori)/N_attacks,
)
table_data
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
row_spec(which(table_data$efficienza >= 0.3), background = "lightgreen") %>%
row_spec(which(table_data$efficienza > 0.2 & table_data$efficienza < 0.3), background = "yellow") %>%
row_spec(which(table_data$efficienza <= 0.2), background = "lightcoral")
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Attacco_tab.html")
fig <- plot_ly(table_data,
x = ~positività,
y = ~efficienza,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Attacchi</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_attacks, sizemode = "area", sizeref = 0.01, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Attacco',
xaxis = list(title = 'Positività', showgrid = FALSE),
yaxis = list(title = 'Efficienza', showgrid = FALSE)
)
fig
saveWidget(fig, "Attacco.html")
LS0tDQp0aXRsZTogIkN1bmVvIERhdGEgQW5hbHlzaXMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIEVhY2ggc2VjdGlvbiBvZiB0aGUgY29kZSBpcyB0aGVuIGV4cGxhaW5lZC4NCg0KRmlyc3Qgb2YgYWxsIGltcG9ydCB0aGUgbGlicmFyaWVzIG5lZWRlZA0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKGMoImRhdGF2b2xsZXkiLCAib3ZseXRpY3MiKSkNCmxpYnJhcnkoZGF0YXZvbGxleSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KG92bHl0aWNzKQ0KYGBgDQoNCkltcG9ydCB0aGUgZmlsZSB5b3UgYXJlIGludGVyZXN0ZWQgaW4gY29uc2lkZXJpbmcgbW9yZSB0aGFuIG9uZSBtYXRjaCwgeW91IGhhdmUgdG8gaW1wb3J0IGFsbCB0aGUgZm9sZGVyDQoNCmBgYHtyfQ0KZmlsZW5hbWUgPC0gIkM6L1VzZXJzL21pcmtvL0RvY3VtZW50cy9HaXRIdWIvQ3VuZW9XZWJzaXRlLmlvL0Fzc2V0cy9DdW5lby1CZXJnYW1vX2N1bmVvLmR2dyINCiNkIDwtIGRpcigiQzovVXNlcnMvbWlya28vT25lRHJpdmUgLSBQb2xpdGVjbmljbyBkaSBNaWxhbm8vQWx0cm8vVm9sbGV5L0NvbmNvMjMyNC9QYXJlbGxhIFRvcmluby9SaXRvcm5vLyIsIHBhdHRlcm4gPSAiZHZ3JCIsIGZ1bGwubmFtZXMgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KdGVhbU5hbWUgPSAnSE9OREEgT0xJVkVSTyBTLkJFUk5BUkRPIENVTkVPJw0KeCA8LSBkdl9yZWFkKGZpbGVuYW1lKQ0Kc2VydmVfaWR4IDwtIGZpbmRfc2VydmVzKHBsYXlzKHgpKQ0KdGFibGUocGxheXMoeCkkdGVhbVtzZXJ2ZV9pZHhdKQ0KYGBgDQoNCkZ1bnppb25pIHV0aWxpDQoNCmBgYHtyfQ0KIyMgZmluZCByb3dzIHdoZXJlIGEgc2luZ2xlIHBsYXllciBpcyBvbiBjb3VydA0KcGxheWVyX29uX2NvdXJ0IDwtIGZ1bmN0aW9uKHgsIHRhcmdldF9wbGF5ZXJfaWQsIHRlYW0gPSBOVUxMKSB7DQogIGlmICghaXMubnVsbCh0ZWFtKSkgdGVhbSA8LSBtYXRjaC5hcmcodGVhbSwgYygiaG9tZSIsICJ2aXNpdGluZyIpKQ0KICAjIyAndGVhbScgaXMgb3B0aW9uYWwgaGVyZSwgaWYgTlVMTCB0aGVuIHdlIGxvb2sgYXQgYm90aCBob21lIGFuZCB2aXNpdGluZyB0ZWFtcw0KICBpZHggPC0gcmVwKEZBTFNFLCBucm93KHgpKQ0KICBpZiAoaXMubnVsbCh0ZWFtKSB8fCB0ZWFtID09ICJob21lIikgew0KICAgIGlkeCA8LSBpZHggfCB4JGhvbWVfcGxheWVyX2lkMSA9PSB0YXJnZXRfcGxheWVyX2lkIHwgeCRob21lX3BsYXllcl9pZDIgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkaG9tZV9wbGF5ZXJfaWQzID09IHRhcmdldF9wbGF5ZXJfaWQgfA0KICAgICAgICAgICAgICAgICB4JGhvbWVfcGxheWVyX2lkNCA9PSB0YXJnZXRfcGxheWVyX2lkIHwgeCRob21lX3BsYXllcl9pZDUgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkaG9tZV9wbGF5ZXJfaWQ2ID09IHRhcmdldF9wbGF5ZXJfaWQNCiAgfQ0KICBpZiAoaXMubnVsbCh0ZWFtKSB8fCB0ZWFtID09ICJ2aXNpdGluZyIpIHsNCiAgICBpZHggPC0gaWR4IHwgeCR2aXNpdGluZ19wbGF5ZXJfaWQxID09IHRhcmdldF9wbGF5ZXJfaWQgfCB4JHZpc2l0aW5nX3BsYXllcl9pZDIgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkdmlzaXRpbmdfcGxheWVyX2lkMyA9PSB0YXJnZXRfcGxheWVyX2lkIHwNCiAgICAgICAgICAgICAgICAgeCR2aXNpdGluZ19wbGF5ZXJfaWQ0ID09IHRhcmdldF9wbGF5ZXJfaWQgfCB4JHZpc2l0aW5nX3BsYXllcl9pZDUgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkdmlzaXRpbmdfcGxheWVyX2lkNiA9PSB0YXJnZXRfcGxheWVyX2lkDQogIH0NCiAgaWR4W2lzLm5hKGlkeCldIDwtIEZBTFNFDQogIGlkeA0KfQ0KDQojIyBmaW5kIHJvd3Mgd2hlcmUgYW55IG9mIG91ciB0YXJnZXQgcGxheWVycyBhcmUgb24gY291cnQNCmFueV9wbGF5ZXJfb25fY291cnQgPC0gZnVuY3Rpb24oeCwgdGFyZ2V0X3BsYXllcl9pZHMsIHRlYW0gPSBOVUxMKSB7DQogICMjIGZvciBlYWNoIHRhcmdldCBwbGF5ZXIsIGZpbmQgcm93cyB3aGVyZSB0aGV5IGFyZSBvbiBjb3VydA0KICBvdXQgPC0gbGFwcGx5KHRhcmdldF9wbGF5ZXJfaWRzLCBmdW5jdGlvbihwaWQpIHBsYXllcl9vbl9jb3VydCh4LCB0YXJnZXRfcGxheWVyX2lkID0gcGlkLCB0ZWFtID0gdGVhbSkpDQogICMjIGFuZCBub3cgZmluZCByb3dzIHdoZXJlIEFOWSBvZiB0aG9zZSBwbGF5ZXJzIHdlcmUgb24gY291cnQNCiAgYXBwbHkoZG8uY2FsbChjYmluZCwgb3V0KSwgMSwgYW55KQ0KfQ0KDQojIyBmaW5kIHJvd3Mgd2hlcmUgYWxsIG9mIG91ciB0YXJnZXQgcGxheWVycyBhcmUgb24gY291cnQNCmFsbF9wbGF5ZXJzX29uX2NvdXJ0IDwtIGZ1bmN0aW9uKHgsIHRhcmdldF9wbGF5ZXJfaWRzLCB0ZWFtID0gTlVMTCkgew0KICAjIyBmb3IgZWFjaCB0YXJnZXQgcGxheWVyLCBmaW5kIHJvd3Mgd2hlcmUgdGhleSBhcmUgb24gY291cnQNCiAgb3V0IDwtIGxhcHBseSh0YXJnZXRfcGxheWVyX2lkcywgZnVuY3Rpb24ocGlkKSBwbGF5ZXJfb25fY291cnQoeCwgdGFyZ2V0X3BsYXllcl9pZCA9IHBpZCwgdGVhbSA9IHRlYW0pKQ0KICAjIyBhbmQgbm93IGZpbmQgcm93cyB3aGVyZSBBTEwgb2YgdGhvc2UgcGxheWVycyB3ZXJlIG9uIGNvdXJ0DQogIGFwcGx5KGRvLmNhbGwoY2JpbmQsIG91dCksIDEsIGFsbCkNCn0NCg0KYGBgDQoNCmBgYHtyfQ0KZCA8LSBkaXIoIkM6L1VzZXJzL21pcmtvL0RvY3VtZW50cy9HaXRIdWIvQ3VuZW9XZWJzaXRlLmlvL0Fzc2V0cy8iLCBwYXR0ZXJuID0gImR2dyQiLCBmdWxsLm5hbWVzID0gVFJVRSkNCmx4IDwtIGxpc3QoKQ0KIyMgcmVhZCBlYWNoIGZpbGUNCmZvciAoZmkgaW4gc2VxX2Fsb25nKGQpKSBseFtbZmldXSA8LSBkdl9yZWFkKGRbZmldLCBpbnNlcnRfdGVjaG5pY2FsX3RpbWVvdXRzID0gRkFMU0UpDQojIyBub3cgZXh0cmFjdCB0aGUgcGxheS1ieS1wbGF5IGNvbXBvbmVudCBmcm9tIGVhY2ggYW5kIGJpbmQgdGhlbSB0b2dldGhlcg0KcHggPC0gbGlzdCgpDQpmb3IgKGZpIGluIHNlcV9hbG9uZyhseCkpIHB4W1tmaV1dIDwtIHBsYXlzKGx4W1tmaV1dKQ0KcHggPC0gZG8uY2FsbChyYmluZCwgcHgpDQoNCmBgYA0KDQojIyBSZW5kaW1lbnRvIGluIEJhdHR1dGENCg0KYGBge3J9DQojLCBlbmRfem9uZSA9PSA1DQp0YWJsZV9kYXRhIDwtIHB4ICU+JSANCiAgZHBseXI6OmZpbHRlcihza2lsbCA9PSAiU2VydmUiLCB0ZWFtID09IHRlYW1OYW1lKSAlPiUgDQogIGdyb3VwX2J5KHBsYXllcl9uYW1lKSAlPiUgDQogIGRwbHlyOjpzdW1tYXJpemUoDQogICAgTl9iYXR0dXRlID0gbigpLA0KICAgIGNvdW50X3BlcmZldHRlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiIyIsIG5hLnJtID0gVFJVRSksDQogICAgY291bnRfcG9zaXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIrIiwgbmEucm0gPSBUUlVFKSwNCiAgICAjY291bnRfZXNjYWxhbWF0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiISIsIG5hLnJtID0gVFJVRSksDQogICAgI2NvdW50X25lZ2F0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiLSIsIG5hLnJtID0gVFJVRSksDQogICAgY291bnRfZXJyb3JpID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiPSIsIG5hLnJtID0gVFJVRSksDQogICAgcG9zaXRpdml0w6AgPSAoY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSkvTl9iYXR0dXRlLA0KICAgIGVmZmljaWVuemEgPSAoY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSAtIGNvdW50X2Vycm9yaSkvTl9iYXR0dXRlLA0KICApDQoNCnRhYmxlX2RhdGENCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoa2FibGVFeHRyYSkNCg0KIyBBcHBseSBjdXN0b20gQ1NTIHN0eWxpbmcgdG8gdGhlIGVudGlyZSB0YWJsZQ0KIyBSZW9yZGVyIGNvbHVtbnMgdG8gbWFrZSBwb3NpdGl2aXTDoCB0aGUgc2Vjb25kIGNvbHVtbg0KdGFibGVfZGF0YSA8LSB0YWJsZV9kYXRhICU+JQ0KICAjc2VsZWN0KC1lZmZpY2llbnphKSAlPiUNCiAgc2VsZWN0KDEsIDcsIGV2ZXJ5dGhpbmcoKSkNCg0KIyBBcHBseSBjdXN0b20gQ1NTIHN0eWxpbmcgdG8gdGhlIGVudGlyZSB0YWJsZQ0Kc3R5bGVkX3RhYmxlIDwtIHRhYmxlX2RhdGEgJT4lDQogIGthYmxlKCJodG1sIikgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBodG1sdGFibGVfY2xhc3MgPSAnc3R5bGVkLXRhYmxlJywgaHRtbF9mb250ID0gJyJCZSBWaWV0bmFtIFBybyIsIHNhbnMtc2VyaWYnKSAlPiUNCiAgY29sdW1uX3NwZWMoMiwgYmFja2dyb3VuZCA9IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gMC4zLCAibGlnaHRncmVlbiIsaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IDAuMiAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IDAuMywgInllbGxvdyIsICJsaWdodGNvcmFsIikpKQ0KDQogICNyb3dfc3BlYyh3aGljaCh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiAwLjIgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCAwLjMpLCBiYWNrZ3JvdW5kID0gInllbGxvdyIpICU+JQ0KICAjcm93X3NwZWMod2hpY2godGFibGVfZGF0YSRlZmZpY2llbnphIDw9IDAuMiksIGJhY2tncm91bmQgPSAibGlnaHRjb3JhbCIpDQoNCiMgU2F2ZSB0aGUgc3R5bGVkIHRhYmxlIHRvIGFuIEhUTUwgZmlsZQ0Kd3JpdGVMaW5lcyhhcy5jaGFyYWN0ZXIoc3R5bGVkX3RhYmxlKSwgIkJhdHR1dGFfdGFiLmh0bWwiKQ0KDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHBsb3RseSkNCg0KZmlnIDwtIHBsb3RfbHkodGFibGVfZGF0YSwgDQogICAgICAgICAgICAgICB4ID0gfnBvc2l0aXZpdMOgLCANCiAgICAgICAgICAgICAgIHkgPSB+ZWZmaWNpZW56YSwNCiAgICAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIA0KICAgICAgICAgICAgICAgbW9kZSA9ICdtYXJrZXJzJywgDQogICAgICAgICAgICAgICBob3ZlcnRlbXBsYXRlID0gcGFzdGUoJzxpPlBsYXllcjwvaT46ICV7dGV4dH0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+UG9zaXRpdml0w6A8L2I+OiAle3h9JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPkVmZmljaWVuemE8L2I+OiAle3l9JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPkJhdHR1dGE8L2I+OiAle21hcmtlci5zaXplfTxleHRyYT48L2V4dHJhPicpLA0KICAgICAgICAgICAgICAgY29sb3IgPSB+cG9zaXRpdml0w6AsDQogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSB+Tl9iYXR0dXRlLCBzaXplbW9kZSA9ICJhcmVhIiwgc2l6ZXJlZiA9IDAuMDA1LCBvcGFjaXR5ID0gMC41KSwNCiAgICAgICAgICAgICAgIHRleHQgPSB+cGxheWVyX25hbWUNCikNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gJ1F1YWxpdMOgIEJhdHR1dGEnLA0KICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdQb3NpdGl2aXTDoCcsIHNob3dncmlkID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ0VmZmljaWVuemEnLCBzaG93Z3JpZCA9IFRSVUUpDQopDQoNCmZpZw0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShodG1sd2lkZ2V0cykNCg0KIyBBc3N1bWluZyAnZmlnJyBpcyB5b3VyIFBsb3RseSBmaWd1cmUNCnNhdmVXaWRnZXQoZmlnLCAiQmF0dHV0YS5odG1sIikNCmBgYA0KDQojIyBSZW5kaW1lbnRvIGluIFJpY2V6aW9uZQ0KDQpPcmEgYW5hbGl6emlhbW8gbGEgcmljZXppb25lOg0KDQpgYGB7cn0NCiMsIGVuZF96b25lID09IDUNCnRhYmxlX2RhdGEgPC0gcHggJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHNraWxsID09ICJSZWNlcHRpb24iLCB0ZWFtID09IHRlYW1OYW1lKSAlPiUgDQogIGdyb3VwX2J5KHBsYXllcl9uYW1lKSAlPiUgDQogIGRwbHlyOjpzdW1tYXJpemUoDQogICAgTl9yZWNlcHRpb25zID0gbigpLA0KICAgIGNvdW50X3BlcmZldHRlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiIyIsIG5hLnJtID0gVFJVRSksDQogICAgY291bnRfcG9zaXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIrIiwgbmEucm0gPSBUUlVFKSwNCiAgICAjY291bnRfZXNjYWxhbWF0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiISIsIG5hLnJtID0gVFJVRSksDQogICAgI2NvdW50X25lZ2F0aXZlID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiLSIsIG5hLnJtID0gVFJVRSksDQogICAgY291bnRfZXJyb3JpID0gc3VtKGV2YWx1YXRpb25fY29kZSA9PSAiPSIsIG5hLnJtID0gVFJVRSksDQogICAgcG9zaXRpdml0w6AgPSAoY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSkvTl9yZWNlcHRpb25zLA0KICAgIGVmZmljaWVuemEgPSAoY291bnRfcG9zaXRpdmUgKyBjb3VudF9wZXJmZXR0ZSAtIGNvdW50X2Vycm9yaSkvTl9yZWNlcHRpb25zLA0KICApDQoNCnRhYmxlX2RhdGENCmBgYA0KDQpgYGB7cn0NCnRhYmxlX2RhdGEgPC0gdGFibGVfZGF0YSAlPiUNCiAgI3NlbGVjdCgtZWZmaWNpZW56YSkgJT4lDQogIHNlbGVjdCgxLCA3LCBldmVyeXRoaW5nKCkpDQoNCiMgQXBwbHkgY3VzdG9tIENTUyBzdHlsaW5nIHRvIHRoZSBlbnRpcmUgdGFibGUNCnN0eWxlZF90YWJsZSA8LSB0YWJsZV9kYXRhICU+JQ0KICBrYWJsZSgiaHRtbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgaHRtbHRhYmxlX2NsYXNzID0gJ3N0eWxlZC10YWJsZScsIGh0bWxfZm9udCA9ICciQmUgVmlldG5hbSBQcm8iLCBzYW5zLXNlcmlmJykgJT4lDQogIGNvbHVtbl9zcGVjKDIsIGJhY2tncm91bmQgPSBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID49IDAuMywgImxpZ2h0Z3JlZW4iLGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiAwLjIgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCAwLjMsICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSkNCg0KIyBTYXZlIHRoZSBzdHlsZWQgdGFibGUgdG8gYW4gSFRNTCBmaWxlDQp3cml0ZUxpbmVzKGFzLmNoYXJhY3RlcihzdHlsZWRfdGFibGUpLCAiUmljZXppb25lX3RhYi5odG1sIikNCmBgYA0KDQpgYGB7cn0NCmZpZyA8LSBwbG90X2x5KHRhYmxlX2RhdGEsIA0KICAgICAgICAgICAgICAgeCA9IH5wb3NpdGl2aXTDoCwgDQogICAgICAgICAgICAgICB5ID0gfmVmZmljaWVuemEsDQogICAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCANCiAgICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsIA0KICAgICAgICAgICAgICAgaG92ZXJ0ZW1wbGF0ZSA9IHBhc3RlKCc8aT5QbGF5ZXI8L2k+OiAle3RleHR9JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPlBvc2l0aXZpdMOgPC9iPjogJXt4fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5FZmZpY2llbnphPC9iPjogJXt5fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5SaWNlemlvbmU8L2I+OiAle21hcmtlci5zaXplfTxleHRyYT48L2V4dHJhPicpLA0KICAgICAgICAgICAgICAgY29sb3IgPSB+cG9zaXRpdml0w6AsDQogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSB+Tl9yZWNlcHRpb25zLCBzaXplbW9kZSA9ICJhcmVhIiwgc2l6ZXJlZiA9IDAuMDA1LCBvcGFjaXR5ID0gMC41KSwNCiAgICAgICAgICAgICAgIHRleHQgPSB+cGxheWVyX25hbWUNCikNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gJ1F1YWxpdMOgIFJpY2V6aW9uZScsDQogICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1Bvc2l0aXZpdMOgJywgc2hvd2dyaWQgPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnRWZmaWNpZW56YScsIHNob3dncmlkID0gVFJVRSkNCikNCmZpZw0KYGBgDQoNCmBgYHtyfQ0Kc2F2ZVdpZGdldChmaWcsICJSaWNlemlvbmUuaHRtbCIpDQpgYGANCg0KIyMgUmVuZGltZW50byBpbiBBdHRhY2NvDQoNCmBgYHtyfQ0KIyAgZW5kX3pvbmUgPT0gNQ0KdGFibGVfZGF0YSA8LSBweCAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoc2tpbGwgPT0gIkF0dGFjayIsIHRlYW0gPT0gdGVhbU5hbWUpICU+JSANCiAgZ3JvdXBfYnkocGxheWVyX25hbWUpICU+JSANCiAgZHBseXI6OnN1bW1hcml6ZSgNCiAgICBOX2F0dGFja3MgPSBuKCksDQogICAgY291bnRfcGVyZmV0dGUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIjIiwgbmEucm0gPSBUUlVFKSwNCiAgICBjb3VudF9wb3NpdGl2ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIisiLCBuYS5ybSA9IFRSVUUpLA0KICAgICNjb3VudF9lc2NhbGFtYXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIhIiwgbmEucm0gPSBUUlVFKSwNCiAgICAjY291bnRfbmVnYXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICItIiwgbmEucm0gPSBUUlVFKSwNCiAgICBjb3VudF9lcnJvcmkgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICI9IiwgbmEucm0gPSBUUlVFKSwNCiAgICBwb3NpdGl2aXTDoCA9IChjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlKS9OX2F0dGFja3MsDQogICAgZWZmaWNpZW56YSA9IChjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlIC0gY291bnRfZXJyb3JpKS9OX2F0dGFja3MsDQogICkNCg0KdGFibGVfZGF0YQ0KYGBgDQoNCmBgYHtyfQ0KdGFibGVfZGF0YSA8LSB0YWJsZV9kYXRhICU+JQ0KICAjc2VsZWN0KC1lZmZpY2llbnphKSAlPiUNCiAgc2VsZWN0KDEsIDcsIGV2ZXJ5dGhpbmcoKSkNCg0KIyBBcHBseSBjdXN0b20gQ1NTIHN0eWxpbmcgdG8gdGhlIGVudGlyZSB0YWJsZQ0Kc3R5bGVkX3RhYmxlIDwtIHRhYmxlX2RhdGEgJT4lDQogIGthYmxlKCJodG1sIikgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBodG1sdGFibGVfY2xhc3MgPSAnc3R5bGVkLXRhYmxlJywgaHRtbF9mb250ID0gJyJCZSBWaWV0bmFtIFBybyIsIHNhbnMtc2VyaWYnKSAlPiUNCiAgY29sdW1uX3NwZWMoMiwgYmFja2dyb3VuZCA9IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gMC4zLCAibGlnaHRncmVlbiIsaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IDAuMiAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IDAuMywgInllbGxvdyIsICJsaWdodGNvcmFsIikpKQ0KDQojIFNhdmUgdGhlIHN0eWxlZCB0YWJsZSB0byBhbiBIVE1MIGZpbGUNCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKHN0eWxlZF90YWJsZSksICJBdHRhY2NvX3RhYi5odG1sIikNCmBgYA0KDQpgYGB7cn0NCmZpZyA8LSBwbG90X2x5KHRhYmxlX2RhdGEsIA0KICAgICAgICAgICAgICAgeCA9IH5wb3NpdGl2aXTDoCwgDQogICAgICAgICAgICAgICB5ID0gfmVmZmljaWVuemEsDQogICAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLCANCiAgICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsIA0KICAgICAgICAgICAgICAgaG92ZXJ0ZW1wbGF0ZSA9IHBhc3RlKCc8aT5QbGF5ZXI8L2k+OiAle3RleHR9JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPkF0dGFjY2hpPC9iPjogJXttYXJrZXIuc2l6ZX08ZXh0cmE+PC9leHRyYT4nKSwNCiAgICAgICAgICAgICAgIGNvbG9yID0gfnBvc2l0aXZpdMOgLA0KICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gfk5fYXR0YWNrcywgc2l6ZW1vZGUgPSAiYXJlYSIsIHNpemVyZWYgPSAwLjAxLCBvcGFjaXR5ID0gMC41KSwNCiAgICAgICAgICAgICAgIHRleHQgPSB+cGxheWVyX25hbWUNCikNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gJ1F1YWxpdMOgIEF0dGFjY28nLA0KICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdQb3NpdGl2aXTDoCcsIHNob3dncmlkID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdFZmZpY2llbnphJywgc2hvd2dyaWQgPSBGQUxTRSkNCikNCg0KZmlnDQpgYGANCg0KYGBge3J9DQpzYXZlV2lkZ2V0KGZpZywgIkF0dGFjY28uaHRtbCIpDQpgYGANCg==